package online.inote.common.aspect;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import online.inote.common.annotation.AutoValid;
import online.inote.common.annotation.AutoValids;
import online.inote.common.utils.ResultFactory;
import online.inote.common.utils.ResultFactory.ResultEnum;
import online.inote.common.utils.StringUtils;

/**
 * <p>
 * Desc: 
 * </p>
 *
 * @author XQF SUI
 * @created 2018年9月10日 上午10:49:20
 * @version 1.0
 */
@Order
@Aspect
@Component
public class AutoValidAspect {
	
	private Logger logger = LoggerFactory.getLogger(getClass());
	
	@Around(value = "@annotation(valids)")
	public Object aroundValids(ProceedingJoinPoint point, AutoValids valids) {
		
		try {
			Set<ConstraintViolation<Object>> violationSet = new HashSet<>();
			
			for (AutoValid valid : valids.value()) {
				if (ArrayUtils.isNotEmpty(point.getArgs()) && point.getArgs()[0] != null) {
					
					Object object = JSON.parseObject(getParams(point, valid), valid.entity());
					
					if (ArrayUtils.isEmpty(valid.groups())) {
						violationSet.addAll(Validation.buildDefaultValidatorFactory().getValidator().validate(object));
					} else {
						violationSet.addAll(Validation.buildDefaultValidatorFactory().getValidator().validate(object, valid.groups()));
					}
				}
			}
			
			if (CollectionUtils.isEmpty(violationSet)) {
				return point.proceed();
			} else {
				return returnResult(point, violationSet);
			}
		} catch (Throwable e) {
			e.printStackTrace();
			logger.error("调用方法失败", e.getMessage());
			return returnResult(point, ResultEnum.FAIL);
		}
	}
	
	@Around(value = "@annotation(valid)")
	public Object aroundValid(ProceedingJoinPoint point, AutoValid valid) {
		
		try {
			Set<ConstraintViolation<Object>> set = null;
			if (ArrayUtils.isNotEmpty(point.getArgs()) && point.getArgs()[0] != null) {
				Object object = JSON.parseObject(getParams(point, valid), valid.entity());
				
				if (ArrayUtils.isEmpty(valid.groups())) {
					set = Validation.buildDefaultValidatorFactory().getValidator().validate(object);
				} else {
					set = Validation.buildDefaultValidatorFactory().getValidator().validate(object, valid.groups());
				}
				
				
			}
			
			if (CollectionUtils.isEmpty(set)) {
				return point.proceed();
			} else {
				return returnResult(point, set);
			}
		} catch (Throwable e) {
			e.printStackTrace();
			logger.error("调用方法失败", e.getMessage());
			return returnResult(point, ResultEnum.FAIL);
		}
	}
	
	private String getParams(ProceedingJoinPoint point, AutoValid valid) {
		
		if (StringUtils.isBlank(valid.key())) {
			return JSON.toJSONString(point.getArgs()[0]);
		} else {
			JSONObject object = JSON.parseObject(point.getArgs()[0].toString());
			
			if (object.containsKey(valid.key())) {
				return object.getString(valid.key());
			}
		}
		
		return "{}";
	}
	
	/**
	 * <p>
	 * Desc: 返回结果
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月3日 下午5:51:04
	 * @param point
	 * @param resultEnum
	 * @return
	 */
	public Object returnResult(ProceedingJoinPoint point, Set<ConstraintViolation<Object>> violationSet) {
		List<String> messageList = new ArrayList<>();
		
		if (CollectionUtils.isNotEmpty(violationSet)) {
			for (ConstraintViolation<Object> violation : violationSet) {
				messageList.add(violation.getMessage());
			}
		}
		
		return returnResult(point, ResultEnum.FAIL, StringUtils.join(messageList, ","));
	}
	
	/**
	 * <p>
	 * Desc: 返回结果(目前项目只有返回Map和String, 如以后有添加其他类型返回值, 需在此处增加类型处理, 以便于更好的前端交互)
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年1月3日 下午5:51:04
	 * @param point
	 * @param resultEnum
	 * @return
	 */
	public Object returnResult(ProceedingJoinPoint point, ResultEnum resultEnum) {
		Class<?> returnType = ((MethodSignature) point.getSignature()).getReturnType(); 
		
		if (returnType == Map.class) {
			return ResultFactory.createResultMap(resultEnum);
		} else if (returnType == String.class) {
			return ResultFactory.createResultJSON(resultEnum);
		} else {
			return null;
		}
	}
	
	/**
	 * <p>
	 * Desc: 返回结果(目前项目只有返回Map和String, 如以后有添加其他类型返回值, 需在此处增加类型处理, 以便于更好的前端交互)
	 * </p>
	 *
	 * @author XQF SUI
	 * @created 2018年9月10日 下午6:48:58
	 * @param point
	 * @param resultEnum
	 * @param customizedMessage
	 * @return
	 */
	public Object returnResult(ProceedingJoinPoint point, ResultEnum resultEnum, String customizedMessage) {
		Class<?> returnType = ((MethodSignature) point.getSignature()).getReturnType(); 
		
		if (returnType == Map.class) {
			return ResultFactory.createResultMap(resultEnum, customizedMessage);
		} else if (returnType == String.class) {
			return ResultFactory.createResultJSON(resultEnum, customizedMessage);
		} else {
			return null;
		}
	}
}